package com.sun.module_base.app

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sun.module_base.network.error.ExceptionHandle
import com.sun.module_network.ApiException
import com.sun.module_network.BaseResponse
import com.sun.module_network.Error
import com.sun.module_network.createException
import com.sun.module_network.utils.loge
import com.sun.module_network.utils.logi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import kotlin.system.exitProcess

/**
 *@author:sunc
 *@time :2025/7/18 16:27
 *@desc: 描述
 */
open class BaseViewModel : ViewModel() {
    val exitTme by mutableLongStateOf(0L)

    val loadingChange: UiLoadingChange by lazy { UiLoadingChange() }

    /**
     * 退出应用
     */
    fun exitApp() {
        exitProcess(0)
    }

    /**
     * 内置封装好的可通知Activity/fragment 显示隐藏加载框 因为需要跟网络请求显示隐藏loading配套才加的，不然我加他个鸡儿加
     */
    inner class UiLoadingChange {
        //显示加载框
        val loadingMessage   by lazy { MutableStateFlow("") }

        //隐藏
        val loadingState by lazy { MutableStateFlow(false) }
    }

    /**
     * 发起网络请求，不校验请求结果数据是否是成功
     *
     * @param block 请求体方法，是一个挂起函数，返回 BaseResponse<T> 类型的响应结果
     * @param resultState 请求回调的 ResultState 数据，用于收集和处理请求状态
     * @param onCompletion 请求完成时的回调函数，默认为空实现
     * @param isShowDialog 是否显示加载对话框，默认为 false
     * @param loadingMessage 加载对话框的提示内容，默认为 "请求网络中..."
     * @return Job 返回一个 Job 对象，可用于管理协程的生命周期
     *
     * @note 该函数使用协程进行网络请求，包含以下特性：
     *       - 使用 Dispatchers.Main 切换到主线程执行
     *       - 设置 30 秒的超时时间
     *       - 使用 flowOn 切换到 IO 线程执行网络请求
     *       - 支持显示/隐藏加载对话框
     *       - 包含异常捕获和处理机制
     *       - 支持请求完成回调
     *       - 自动处理请求结果的状态更新
     */
    fun <T> launchUI2(
        block: suspend () -> BaseResponse<T>,
        resultState: MutableStateFlow<ResultState<T>>,
        onCompletion: () -> Unit = {},
        isShowDialog: Boolean = false,
        loadingMessage: String = "请求网络中...",
    ): Job {
        return viewModelScope.launch(Dispatchers.Main) {
            // 统一管理对话框状态
            val dialogController = DialogController(isShowDialog, loadingMessage)

            try {
                // 显示对话框
                dialogController.show()

                // 执行网络请求
                val response = withTimeout(30 * 1000) {
                    flow { emit(block()) }
                        .flowOn(Dispatchers.IO)
                        .single()
                }

                // 处理响应结果
                withContext(Dispatchers.Main) {
                    dialogController.hide()
                    resultState.parseResult(response)
                }

            } catch (e: Exception) {
                // 统一错误处理
                withContext(Dispatchers.Main) {
                    dialogController.hide()
                    "网络请求异常：${e.message ?: "未知错误"}".loge()
                    resultState.parseException(e)
                }
            } finally {
                // 确保完成回调被调用
                onCompletion()
            }
        }
    }

    /**
     * DialogController 是一个用于控制对话框显示和隐藏的控制器类。
     *
     * 该类主要用于管理对话框的显示状态，支持异步操作，确保在主线程上更新UI。
     * 核心功能包括显示对话框(show)和隐藏对话框(hide)。
     *
     * 使用示例:
     * ```kotlin
     * val controller = DialogController(isShowDialog = true, loadingMessage = "加载中...")
     * // 显示对话框
     * controller.show()
     * // 隐藏对话框
     * controller.hide()
     * ```
     *
     * @param isShowDialog 控制是否显示对话框的布尔值，true表示允许显示，false表示不允许显示
     * @param loadingMessage 对话框中显示的加载提示文本内容
     */
    inner class DialogController(
        private val isShowDialog: Boolean,
        private val loadingMessage: String
    ) {
        suspend fun show() {
            if (isShowDialog) {
                withContext(Dispatchers.Main) {
                    loadingChange.loadingMessage.value = loadingMessage
                    loadingChange.loadingState.value = true
                }
            }
        }

        suspend fun hide() {
            if (isShowDialog) {
                withContext(Dispatchers.Main) {
                    loadingChange.loadingState.value = false
                }
            }
        }
    }

    /**
     * 发起网络请求，不校验请求结果数据是否是成功
     *
     * @param block 请求体方法，是一个挂起函数，返回 BaseResponse<T> 类型的响应结果
     * @param resultState 请求回调的 ResultState 数据，用于收集和处理请求状态
     * @param onCompletion 请求完成时的回调函数，默认为空实现
     * @param isShowDialog 是否显示加载对话框，默认为 false
     * @param loadingMessage 加载对话框的提示内容，默认为 "请求网络中..."
     * @return Job 返回一个 Job 对象，可用于管理协程的生命周期
     *
     * @note 该函数使用协程进行网络请求，包含以下特性：
     *       - 使用 Dispatchers.Main 切换到主线程执行
     *       - 设置 30 秒的超时时间
     *       - 使用 flowOn 切换到 IO 线程执行网络请求
     *       - 支持显示/隐藏加载对话框
     *       - 包含异常捕获和处理机制
     *       - 支持请求完成回调
     *       - 自动处理请求结果的状态更新
     */
    fun <T> launchUI(
        block: suspend () -> BaseResponse<T>,
        resultState: MutableStateFlow<ResultState<T>>,
        onCompletion: () -> Unit = {},
        isShowDialog: Boolean = false,
        loadingMessage: String = "请求网络中...",
    ): Job {
        return viewModelScope.launch(Dispatchers.Main) {
            // 统一管理对话框状态
            val dialogController = DialogController(isShowDialog, loadingMessage)
            flow {
                val response = withTimeout(30 * 1000) {
                    block.invoke()
                }
                emit(response)
            }.flowOn(Dispatchers.IO)
                .onStart {
                    dialogController.show()

                }
                .onEach { "resultState 状态变更".logi() }
                .catch {
                    "网络请求异常抓取： ${it.message}".loge()
                    dialogController.hide()
                    resultState.parseException(it)
                }.onCompletion {
                    onCompletion.invoke()
                }.collect {
                    dialogController.hide()
                    resultState.parseResult(it)
                }
        }
    }

    /**
     * 启动一个协程来处理网络请求Flow，不校验请求结果数据是否是成功
     *
     * @param T 请求响应数据的泛型类型
     * @param flow 请求的Flow流，类型为Flow<BaseResponse<T>>
     * @param resultState 用于存储请求结果的MutableStateFlow，类型为MutableStateFlow<ResultState<T>>
     * @param onCompletion 请求完成时的回调函数，默认为空函数
     * @param isShowDialog 是否显示加载对话框，默认为false
     * @param loadingMessage 加载对话框中显示的提示文本，默认为"请求网络中..."
     * @return Job 返回一个Job对象，可用于协程的取消和状态管理
     *
     * @note 该函数在主线程启动协程，网络请求在IO线程执行，结果处理在主线程执行
     * @note 使用flowOn切换请求线程，使用onStart处理加载框显示
     * @note 使用catch捕获异常并处理，使用onCompletion处理完成回调
     * @note 使用collect收集结果并更新resultState
     */
    fun <T> launchUIbyFlow(
        flow: Flow<BaseResponse<T>>,
        resultState: MutableStateFlow<ResultState<T>>,
        onCompletion: () -> Unit = {},
        isShowDialog: Boolean = false,
        loadingMessage: String = "请求网络中...",
    ): Job {
        return viewModelScope.launch(Dispatchers.Main) {
            // 统一管理对话框状态
            val dialogController = DialogController(isShowDialog, loadingMessage)
            flow
                .flowOn(Dispatchers.IO)
                .onStart {
                    dialogController.show()

                }
                .onEach { "resultState 状态变更${it}".logi() }
                .catch {
                    "网络请求异常抓取： ${it.message}".loge()
                    dialogController.hide()
                    resultState.parseException(it)
                }.onCompletion {
                    onCompletion.invoke()
                }.collect {
                    dialogController.hide()
                    resultState.parseResult(it)
                }
        }
    }


    /**
     * 在UI线程启动协程执行网络请求，并处理请求结果
     *
     * 该函数封装了网络请求的基本流程，包括请求执行、结果处理、错误处理和UI更新。
     * 使用协程和Flow实现异步请求，支持自动线程切换和生命周期管理。
     *
     * @param block 挂起函数，执行网络请求并返回BaseResponse<T>结果
     *              必须使用suspend关键字修饰，在IO线程执行
     * @param success 成功回调函数，接收请求返回的数据
     * @param error 失败回调函数，处理请求异常，默认为空实现
     * @param onCompletion 完成回调函数，在请求完成时调用（无论成功失败），默认为空实现
     * @param isShowDialog 是否显示加载对话框，默认为false
     * @param loadingMessage 加载对话框显示的文本内容，默认为"请求网络中..."
     * @return Job 协程任务对象，可用于协程的取消和控制
     *
     * @throws ApiException 当网络请求失败或返回状态码不为成功时抛出
     * @throws TimeoutException 当请求超过30秒超时限制时抛出
     *
     * @sample 示例用法：
     * launchUI(
     *     block = { apiService.getData() },
     *     success = { data -> updateUI(data) },
     *     error = { error -> showError(error) },
     *     isShowDialog = true
     * )
     */
    fun <T> launchUI(
        block: suspend () -> BaseResponse<T>,
        success: (T?) -> Unit,
        error: (ApiException) -> Unit = {},
        onCompletion: () -> Unit = {},
        isShowDialog: Boolean = false,
        loadingMessage: String = "请求网络中..."
    ): Job {
        // 统一管理对话框状态
        val dialogController = DialogController(isShowDialog, loadingMessage)
        return viewModelScope.launch(Dispatchers.Main) {
            flow {
                val response = withTimeout(30 * 1000) {
                    block.invoke()
                }
                emit(response)
            }.flowOn(Dispatchers.IO)
                .onStart {
                    dialogController.show()
                }.onEach { "resultState 状态变更${it}".logi() }
                .catch {
                    //打印错误消息
                    "网络请求异常抓取： ${it.message}".loge()
                    //失败回调
                    error(ExceptionHandle.handlerException(it))
                    //网络请求异常 关闭弹窗
                    dialogController.hide()
                }
                .onCompletion { onCompletion.invoke() }
                .collect {
                    //网络请求异常 关闭弹窗
                    dialogController.hide()
                    executeResponse(it) { t -> success(t) }
                }
        }
    }

    /**
     *  不过滤请求结果
     * @param block 请求体 必须要用suspend关键字修饰
     * @param success 成功回调
     * @param error 失败回调 可不给
     * @param isShowDialog 是否显示加载框
     * @param loadingMessage 加载框提示内容
     */
    fun <T> launchUINoCheck(
        block: suspend () -> T,
        success: (T) -> Unit,
        error: (ApiException) -> Unit = {},
        isShowDialog: Boolean = false,
        loadingMessage: String = "请求网络中...",
    ): Job {
        return viewModelScope.launch {
            // 统一管理对话框状态
            val dialogController = DialogController(isShowDialog, loadingMessage)
            flow {
                val response = withTimeout(30 * 1000) {
                    block.invoke()
                }
                emit(response)
            }.flowOn(Dispatchers.IO)
                .onStart {
                    dialogController.show()
                }.onEach { "resultState 状态变更${it}".logi() }
                .catch {
                    //网络请求异常 关闭弹窗
                    dialogController.hide()
                    error.invoke(ExceptionHandle.handlerException(it))
                }
                .onCompletion { }
                .collect {
                    dialogController.hide()
                    success.invoke(it)
                }
        }
    }

    suspend fun <T> executeResponse(
        response: BaseResponse<T>,
        success: suspend CoroutineScope.(T?) -> Unit
    ) {
        viewModelScope.launch {
            when {
                response.isSuccess() -> {
                    success(response.getResponseData())
                }

                else -> throw Error.HTTP_ERROR.createException(
                    code = response.getResponseCode(),
                    message = response.getResponseMsg()
                )

            }
        }
    }
}

